package com.it.gmall.product.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.it.gmall.common.cache.GmallCache;
import com.it.gmall.common.constant.RedisConst;
import com.it.gmall.model.list.SearchAttr;
import com.it.gmall.model.product.*;
import com.it.gmall.mq.consts.MqConst;
import com.it.gmall.mq.service.SendMsgService;
import com.it.gmall.product.mapper.*;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class ManageServiceImpl implements ManageService{

    // 注入mq发送消息
    @Autowired
    private SendMsgService sendMsgService;

    /**
     * 查看一级分类
     * @return
     */
    @Override
    public List<BaseCategory1> getCategory1() {
        List<BaseCategory1> category1List = baseCategory1Mapper.selectList(null);
        return category1List;
    }

    /**
     * 查看二级分类
     * @param category1Id 一级分类的id
     * @return
     */
    @Override
    public List<BaseCategory2> getCategory2(long category1Id) {
        QueryWrapper<BaseCategory2> wrapper = new QueryWrapper<>();
        wrapper.eq("category1_id",category1Id);
        List<BaseCategory2> category2List = baseCategory2Mapper.selectList(wrapper);
        return category2List;
    }

    /**
     * 查看三级分类
     * @param category2Id 二级分类的id
     * @return
     */
    @Override
    public List<BaseCategory3> getCategory3(long category2Id) {
        QueryWrapper<BaseCategory3> wrapper = new QueryWrapper<>();
        wrapper.eq("category2_id",category2Id);
        List<BaseCategory3> category3List = baseCategory3Mapper.selectList(wrapper);
        return category3List;
    }

    /**
     * 根据分类id获取平台属性
     * @param category1Id
     * @param category2Id
     * @param category3Id
     * @return
     */
    @Override
    public List<BaseAttrInfo> attrInfoList(long category1Id, long category2Id, long category3Id) {
        List<BaseAttrInfo> list = baseAttrInfoMapper.attrInfoList(category1Id,category2Id,category3Id);
        return list;
    }

    /**
     * 添加属性信息
     * @return
     */
    @Override
    public void saveAttrInfo(BaseAttrInfo baseAttrInfo) {
        if(baseAttrInfo.getId()==null){//添加
            // 添加属性表(主键回填)
            baseAttrInfoMapper.insert(baseAttrInfo);

        }else{//修改

            // 查询属性表
            BaseAttrInfo baseAttrInfoById = baseAttrInfoMapper.selectById(baseAttrInfo.getId());
            baseAttrInfoById.setAttrName(baseAttrInfo.getAttrName());
            baseAttrInfoById.setCategoryId(baseAttrInfo.getCategoryId());
            baseAttrInfoById.setCategoryLevel(baseAttrInfo.getCategoryLevel());
            // 修改属性表
            baseAttrInfoMapper.updateById(baseAttrInfoById);

            // 删除属性值表
            QueryWrapper<BaseAttrValue> wrapper = new QueryWrapper<>();
            wrapper.eq("attr_id",baseAttrInfo.getId());
            baseAttrValueMapper.delete(wrapper);

        }

        // 添加属性值表(属性的id是属性值表的外键 一对多的关系)
        List<BaseAttrValue> attrValueList = baseAttrInfo.getAttrValueList();
        if(!CollectionUtils.isEmpty(attrValueList)){
            attrValueList.forEach(attrValue -> {
                attrValue.setAttrId(baseAttrInfo.getId()); // 设置外键
                baseAttrValueMapper.insert(attrValue);
            });
        }

    }

    /**
     * 根据id获取属性对应的属性值
     * @return
     */
    @Override
    public List<BaseAttrValue> getAttrValueList(long attrId) {
        QueryWrapper<BaseAttrValue> wrapper = new QueryWrapper<>();
        wrapper.eq("attr_id",attrId);
        List<BaseAttrValue> list = baseAttrValueMapper.selectList(wrapper);
        return list;
    }

    /**
     * 分页查询商品品牌列表
     * @param page 页码
     * @param limit 条数
     * @return
     */
    @Override
    public IPage<BaseTrademark> baseTrademark(Integer page, Integer limit) {
        IPage<BaseTrademark> iPage = baseTrademarkMapper.selectPage(new Page<>(page,limit),null);
        return iPage;
    }

    /**
     * 添加/修改 品牌信息
     * @param baseTrademark 品牌数据
     * @return
     */
    @Override
    public void save(BaseTrademark baseTrademark) {
            baseTrademarkMapper.insert(baseTrademark);
    }

    /**
     * 根据id获取品牌数据
     * @param tradeMarkId
     * @return
     */
    @Override
    public BaseTrademark get(long tradeMarkId) {
        BaseTrademark baseTrademark = baseTrademarkMapper.selectById(tradeMarkId);
        return baseTrademark;
    }

    /**
     * 根据id删除品牌数据
     * @param tradeMarkId
     */
    @Override
    public void remove(long tradeMarkId) {
        baseTrademarkMapper.deleteById(tradeMarkId);
    }

    /**
     * 修改品牌信息
     * @param baseTrademark 品牌数据
     * @return
     */
    @Override
    public void update(BaseTrademark baseTrademark) {
        baseTrademarkMapper.updateById(baseTrademark);
    }

    /**
     * 获取spu列表
     * @param page
     * @param limit
     * @param category3Id
     * @return
     */
    @Override
    public IPage<SpuInfo> spuList(Integer page, Integer limit, long category3Id) {
        QueryWrapper<SpuInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("category3_id",category3Id);
        IPage<SpuInfo> spuInfoIPage = spuInfoMapper.selectPage(new Page<>(page, limit), wrapper);
        return spuInfoIPage;
    }

    /**
     * 获取品牌列表
     * @return
     */
    @Override
    public List<BaseTrademark> getTrademarkList() {
        List<BaseTrademark> list = baseTrademarkMapper.selectList(null);
        return list;
    }

    /**
     * 获取销售属性列表
     * @return
     */
    @Override
    public List<BaseSaleAttr> baseSaleAttrList() {
        List<BaseSaleAttr> baseSaleAttrs = baseSaleAttrMapper.selectList(null);
        return baseSaleAttrs;
    }

    /**
     * 添加spu
     * @param spuInfo
     * @return
     */
    @Override
    public void saveSpuInfo(SpuInfo spuInfo) {
        spuInfoMapper.insert(spuInfo); // 主键回填

        // 获取图片
        List<SpuImage> spuImageList = spuInfo.getSpuImageList();
        if(!CollectionUtils.isEmpty(spuImageList)){
            spuImageList.forEach(spuImage -> {
                spuImage.setSpuId(spuInfo.getId()); // 外键
                spuImageMapper.insert(spuImage);
            });
        }

        // 获取销售属性
        List<SpuSaleAttr> spuSaleAttrList = spuInfo.getSpuSaleAttrList();
        if(!CollectionUtils.isEmpty(spuSaleAttrList)){
            spuSaleAttrList.forEach(spuSaleAttr ->{ // 外键
                spuSaleAttr.setSpuId(spuInfo.getId());
                spuSaleAttrMapper.insert(spuSaleAttr);

                // 获取销售属性值
                List<SpuSaleAttrValue> spuSaleAttrValueList = spuSaleAttr.getSpuSaleAttrValueList();
                if(!CollectionUtils.isEmpty(spuSaleAttrValueList)){
                    spuSaleAttrValueList.forEach(spuSaleAttrValue -> {
                        spuSaleAttrValue.setSpuId(spuInfo.getId());
                        spuSaleAttrValue.setSaleAttrName(spuSaleAttr.getSaleAttrName());
                        spuSaleAttrValueMapper.insert(spuSaleAttrValue);
                    });
                }
            });
        }
    }

    /**
     * 获取spu的所有图片
     * @param spuId
     * @return
     */
    @Override
    public List<SpuImage> spuImageList(long spuId) {
        QueryWrapper<SpuImage> wrapper = new QueryWrapper<>();
        wrapper.eq("spu_id",spuId);
        List<SpuImage> spuImages = spuImageMapper.selectList(wrapper);
        return spuImages;
    }

    /**
     * 获取spu的所有销售属性
     * @param spuId
     * @return
     */
    @Override
    public List<SpuSaleAttr> spuSaleAttrList(long spuId) {
        QueryWrapper<SpuSaleAttr> wrapper = new QueryWrapper<>();
        wrapper.eq("spu_id",spuId);
        List<SpuSaleAttr> spuSaleAttrList = spuSaleAttrMapper.selectList(wrapper);

        if(!CollectionUtils.isEmpty(spuSaleAttrList)){
            spuSaleAttrList.forEach(spuSaleAttr -> {
                List<SpuSaleAttrValue> spuSaleAttrValues = spuSaleAttrValueMapper.selectList(
                        new QueryWrapper<SpuSaleAttrValue>()
                                .eq("spu_id", spuId)
                                .eq("base_sale_attr_id",spuSaleAttr.getBaseSaleAttrId()));
                spuSaleAttr.setSpuSaleAttrValueList(spuSaleAttrValues);
            });
        }
        return spuSaleAttrList;
    }

    /**
     * 保存商品的sku信息
     * @param skuInfo
     * @return
     */
    @Override
    public void saveSkuInfo(SkuInfo skuInfo) {
        // 保存基本的sku信息
        skuInfoMapper.insert(skuInfo); // 主键回填

        // 获取图片
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if(!CollectionUtils.isEmpty(skuImageList)){
            skuImageList.forEach(skuImage -> {
                skuImage.setSkuId(skuInfo.getId()); // 外键
                skuImageMapper.insert(skuImage);
            });
        }

        // 获取平台属性
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if(!CollectionUtils.isEmpty(skuAttrValueList)){
            skuAttrValueList.forEach(skuAttrValue -> {
                skuAttrValue.setSkuId(skuInfo.getSpuId());
                skuAttrValueMapper.insert(skuAttrValue);
            });
        }

        // 获取销售属性
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if(!CollectionUtils.isEmpty(skuSaleAttrValueList)){
            skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId()); // spuid
                skuSaleAttrValue.setSkuId(skuInfo.getId()); // skuid
                skuSaleAttrValueMapper.insert(skuSaleAttrValue);
            });
        }
    }

    /**
     * 获取sku列表
     * @param page
     * @param limit
     * @return
     */
    @Override
    public IPage<SkuInfo> skuList(Integer page, Integer limit) {
        IPage<SkuInfo> skuInfoIPage = skuInfoMapper.selectPage(new Page<SkuInfo>(page, limit), null);
        return skuInfoIPage;
    }

    /**
     * 上架sku商品
     * @param skuId
     * @return
     */
    @Override
    public void onSale(Integer skuId) {
        QueryWrapper<SkuInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("id",skuId);

        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setIsSale(1); // 上架

        skuInfoMapper.update(skuInfo,wrapper);

        // 上架商品 使用mq通知搜索微服务
        sendMsgService.sendMsg(MqConst.EXCHANGE_DIRECT_GOODS,MqConst.ROUTING_GOODS_UPPER,skuId);
    }

    /**
     * 下架sku商品
     * @param skuId
     * @return
     */
    @Override
    public void cancelSale(Integer skuId) {
        QueryWrapper<SkuInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("id",skuId);

        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setIsSale(0); // 下架

        skuInfoMapper.update(skuInfo,wrapper);

        // 下架商品 使用mq通知搜索微服务
        sendMsgService.sendMsg(MqConst.EXCHANGE_DIRECT_GOODS,MqConst.ROUTING_GOODS_LOWER,skuId);
    }


    /**
     * 查询123级分类对应的视图
     * @param category3Id 3级分类的id
     * @return
     */
    @Override
    @Transactional
    @GmallCache(prefix = "getBaseCategoryView")
    public BaseCategoryView getBaseCategoryView(long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }

    /**
     * 获取商品的sku信息 以及 其所对应的图片列表
     * @param skuId 商品skuid
     * @return
     * 获取商品的sku信息-分布式锁-结合redisson
     */
    @Override
    public SkuInfo getSkuInfo(long skuId) {
        // 定义key
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
        String cacheKeyLock = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;

        // 查询redis中是否有
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);

        // 如果缓存中没有数据
        if(null == skuInfo){
            // 分布式锁-解决缓存击穿
            // 从redisson获取一个锁
            RLock lock = redissonClient.getLock(cacheKeyLock);
            try {
                /*
                * 上锁：过期不候锁
                * 第一个参数：上锁期间可以等待多长时间
                * 第二个参数：锁得有效时间
                * 第三个参数：前面两个时间的单位*/
                boolean isLock = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
                if(isLock){ // 成功上锁 可以进行下一步
                    try {

                        // 从DB查询
                        skuInfo = skuInfoMapper.selectById(skuId);
                        if(null!=skuInfo){ // 避免出现空指针异常
                            List<SkuImage> skuImageList
                                    = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuInfo.getId()));
                            skuInfo.setSkuImageList(skuImageList);

                            redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300),TimeUnit.SECONDS);

                        }else{
                            redisTemplate.opsForValue().set(cacheKey,new SkuInfo(),5, TimeUnit.MINUTES);
                        }
                    } finally {
                        // 释放锁
                        lock.unlock();
                    }
                }else{ // 上锁失败 从缓存中拿取
                    TimeUnit.SECONDS.sleep(500);
                    return (SkuInfo)redisTemplate.opsForValue().get(cacheKey);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        // 返回数据
        return skuInfo;
    }

    /**
     * 获取商品的sku信息 以及 其所对应的图片列表
     * @param skuId 商品skuid
     * @return
     * 获取商品的sku信息-分布式锁版本
     */
    /*@Override
    public SkuInfo getSkuInfo(long skuId) {
        // 定义key
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
        String cacheKeyLock = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;

        // 查询redis中是否有
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);

        // 生成uuid
        String uuid = UUID.randomUUID().toString();
        // 如果缓存中没有数据
        if(null == skuInfo){
            // 解决击穿-上分布式锁
            // 查询redis-是否上了分布式锁 ； 以及设置uuid 防止误删； 以及防止死锁 设置过期时间
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(cacheKeyLock, uuid, 3, TimeUnit.SECONDS);
            if(ifAbsent){//上锁成功
                try{
                    // 查询db
                    skuInfo = skuInfoMapper.selectById(skuId);
                    if(null!=skuInfo){ // db有这条数据
                        List<SkuImage> skuImageList
                                = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuInfo.getId()));
                        skuInfo.setSkuImageList(skuImageList);
                        // 设置数据到redis中； 防止雪崩 设一个过期时间
                        redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300),TimeUnit.SECONDS);
                    }else{
                        // 防止击穿 将null值也设置一份 设较短的过期时间(5分钟)
                        redisTemplate.opsForValue().set(cacheKey,new SkuInfo(),5,TimeUnit.MINUTES);
                    }
                }finally{

                    // 删除分布式锁
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                    this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(cacheKeyLock), uuid);
                }
            }else{// 已经有锁了 不可以在上 查询缓存的数据
                try {
                    TimeUnit.SECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
            }
        }

        // 返回数据
        return skuInfo;
    }*/

    /**
     * 获取商品的sku信息 以及 其所对应的图片列表
     * @param skuId 商品skuid
     * @return
     * 获取商品的信息 - 本地锁版本
     */
    /*private final Lock lock = new ReentrantLock();
    @Override
    public SkuInfo getSkuInfo(long skuId) {
        // 定义key
        String cacheKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;

        // 查询redis中是否有
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);

        *//*解决缓存击穿--一个热点key，在某一个时间段内被高频的访问，此时刚好这个key对应的缓存失效
        * 解决：使用多线程的锁解决
        *      假设100万个请求 有一个拿到锁 就让这个拿到锁的去查询数据库，同时放到redis缓存中
        *      剩下的所有请求 直接去缓存中查询
        *      过期不侯锁使用tryLock 不见不散锁使用lock*//*
        // 如果缓存没有
        if(null == skuInfo){
            try {
                boolean tryLock = lock.tryLock(2, TimeUnit.SECONDS);
                    if(tryLock){ // 这个线程抢到了锁 查询数据并且放到redis中
                        try {

                            // 从DB查询
                            skuInfo = skuInfoMapper.selectById(skuId);
                            if(null!=skuInfo){ // 避免出现空指针异常
                                List<SkuImage> skuImageList
                                        = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuInfo.getId()));
                                skuInfo.setSkuImageList(skuImageList);

                                // 将结果放到redis中 24小时过期
                                //redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT,TimeUnit.MINUTES);

                                *//*解决缓存雪崩--当缓存中的过期时间到了以后，缓存在某一个时刻全部失效，请求全部转发到DB
                                 * 解决：在原有保存基础上 随机增加1-5分钟的过期时间*//*
                                redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT+new Random().nextInt(300),TimeUnit.SECONDS);

                            }else{
                                *//*解决缓存穿透--频繁的访问不存在的数据
                                 * 解决：将null值也存储在redis中，设置较短的过期时间，比如五分钟*//*
                                redisTemplate.opsForValue().set(cacheKey,new SkuInfo(),5, TimeUnit.MINUTES);
                            }
                        } finally {
                            // 释放锁
                            lock.unlock();
                        }
                    }else{ // 这个线程没有抢到锁 直接去redis查询缓存
                        skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                    }}catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 返回数据
        return skuInfo;
    }*/

    /**
     * 查询属性及属性值以及属性值是否选中
     * @param skuId
     * @param spuId
     * @return
     */
    @Override
    @GmallCache(prefix = "getSpuSaleAttrListCheckBySku")
    public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(long skuId, long spuId) {
        return spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId,spuId);
    }

    /**
     * 获取点击销售属性所对应的skuId集合
     * @param spuId
     * @return
     */
    @Override
    @GmallCache(prefix = "getSkuValueIdsMap")
    public Map getSkuValueIdsMap(long spuId) {
        List<Map> listMap = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        Map<Object, Object> map = new HashMap<>();
        listMap.forEach(i -> {
            map.put(i.get("value_ids"),i.get("sku_id"));
        });
        return map;
    }

    /**
     * 获取商品sku的价格
     * @param skuId
     * @return
     */
    @Override
    public BigDecimal getPrice(long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        return skuInfo.getPrice();
    }

    /**
     * 获取全部三级分类视图
     * @return
     */
    @Override
    public List<BaseCategoryView> getBaseCategoryViewList() {
        List<BaseCategoryView> baseCategoryViews = baseCategoryViewMapper.selectList(null);
        return baseCategoryViews;
    }

    /**
     * 获取商品对应的品牌数据
     * @param tmId
     * @return
     */
    @Override
    public BaseTrademark getBaseTrademark(long tmId) {
        return baseTrademarkMapper.selectById(tmId);
    }

    /**
     * 根据商品的skuId获取商品的平台属性
     * @param skuId
     * @return
     */
    @Override
    public List<SkuAttrValue> getSearchAttrList(long skuId) {
        return skuAttrValueMapper.getSearchAttrList(skuId);
    }

    
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;
    @Autowired
    private SkuImageMapper skuImageMapper;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;
    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private SpuImageMapper spuImageMapper;
    @Autowired
    private BaseSaleAttrMapper baseSaleAttrMapper;
    @Autowired
    private SpuInfoMapper spuInfoMapper;
    @Autowired
    private BaseCategory1Mapper baseCategory1Mapper;
    @Autowired
    private BaseCategory2Mapper baseCategory2Mapper;
    @Autowired
    private BaseCategory3Mapper baseCategory3Mapper;
    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;
    @Autowired
    private BaseAttrValueMapper baseAttrValueMapper;
    @Autowired
    private BaseTrademarkMapper baseTrademarkMapper;
}
